package com.swak.fx.support;

import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Map;
import java.util.stream.Stream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.swak.exception.InvokeException;
import com.swak.utils.Lists;
import com.swak.utils.Maps;

import lombok.Getter;

/**
 * 父节点, 子节点的管理能力
 * 
 * @author DELL
 */
@SuppressWarnings({ "rawtypes", "unchecked" })
class Parent {

	protected static final Logger LOGGER = LoggerFactory.getLogger(Parent.class);
	protected static final Class<?>[] Classes = new Class[] { AbstractPage.class };
	protected final Map<Class<? extends Lifecycle>, WeakPages> pages = Maps.newHashMap();

	protected <T extends Lifecycle> T NewPage(Class<T> pageClass) {
		FXMLView view = pageClass.getAnnotation(FXMLView.class);
		try {
			return this.NewPage(pageClass, view != null ? view.singleton() : false, null, null);
		} catch (Throwable e) {
			LOGGER.error("New Page {} Error,", pageClass.getName(), e);
			throw new InvokeException(e);
		}
	}

	protected <T extends Lifecycle> T NewComp(Class<T> pageClass, AbstractPage parent) {
		FXMLView view = pageClass.getAnnotation(FXMLView.class);
		try {
			return this.NewPage(pageClass, view != null ? view.singleton() : false, Classes, new Object[] { parent });
		} catch (Throwable e) {
			LOGGER.error("New Page {} Error,", pageClass.getName(), e);
			throw new InvokeException(e);
		}
	}

	protected synchronized <T extends Lifecycle> T NewPage(Class<T> pageClass, boolean singleton, Class[] cs,
			Object[] ps) throws InstantiationException, IllegalAccessException, IllegalArgumentException,
			InvocationTargetException, SecurityException, NoSuchMethodException {

		// 实例
		T instance = null;

		// 如果是单例判断是否已经创建过，如果没有则直接创建
		if (singleton) {
			WeakPages pages = this.pages.get(pageClass);
			if (pages != null) {
				instance = (T) pages.get();
			}
			if (instance != null && !instance.isDestoryed()) {
				return instance;
			}
		}

		// 实例化
		if (cs != null) {
			instance = (T) pageClass.getDeclaredConstructor(cs).newInstance(ps);
		} else {
			instance = (T) pageClass.getDeclaredConstructor().newInstance();
		}

		// 初始化流程
		instance.init();

		// 保存实例
		WeakPages pages = this.pages.computeIfAbsent(pageClass, (key) -> {
			return new WeakPages();
		});
		pages.add(instance);

		// 返回实例
		return instance;
	}

	public Stream<Lifecycle> stream() {
		return this.pages.values().stream().flatMap(page -> page.stream());
	}

	public void destory() {
		pages.forEach((k, wps) -> {
			wps.destory();
		});
	}

	public void destory(Lifecycle page) {
		WeakPages thePages = pages.get(page.getClass());
		if (thePages != null) {
			thePages.destory(page);
		}
	}

	@Getter
	public class WeakPages<T extends Lifecycle> {

		ArrayList<WeakPage<T>> pages;
		ReferenceQueue<? extends Lifecycle> queue;
		int removed = 0;

		public T get() {
			return pages == null || pages.isEmpty() ? null : pages.get(0).get();
		}

		public WeakPages<T> add(T t) {
			if (pages == null) {
				pages = Lists.newArrayList(1);
				queue = new ReferenceQueue<T>();
			}

			this.removeExpires();
			WeakPage<T> wPage = new WeakPage(pages.size(), t, queue);
			pages.add(wPage);
			return this;
		}

		public WeakPages<T> destory(T t) {
			if (pages != null) {
				try {
					t.destory();
				} catch (Exception e) {
				}
				int foundIndex = -1;
				for (WeakPage p : pages) {
					if (p.get() != null && p.get() == t) {
						foundIndex = p.index;
					}
				}
				if (foundIndex != -1) {
					pages.remove(foundIndex);
				}
			}
			return this;
		}

		public Stream<T> stream() {
			return pages.stream().map(ref -> ref.get());
		}

		private void removeExpires() {
			for (Object x; (x = queue.poll()) != null;) {
				WeakPage<T> wp = (WeakPage<T>) x;
				this.pages.remove(wp.index);
				removed++;
			}

			// 删除的数据超过指定数量后才缩小容量
			if (removed > 32) {
				pages.trimToSize();
				removed = 0;
			}
		}

		private void destory() {
			if (pages != null) {
				pages.forEach((wp) -> {
					T t = wp.get();
					if (t != null) {
						try {
							t.destory();
						} catch (Exception e) {
						}
					}
				});
			}
		}
	}

	@Getter
	public class WeakPage<T extends Lifecycle> extends WeakReference<T> {

		private int index;

		public WeakPage(int index, T referent, ReferenceQueue<T> queue) {
			super(referent, queue);
		}
	}
}